﻿using System;
using PT.Common;

namespace PT.Patterns {
    public class AccuracyEvaluator {

        public long QuarternotePeriod { get; set; }

        public AccuracyEvaluator(long quarternotePeriod) {
            QuarternotePeriod = quarternotePeriod;
        }

        /// <summary>Finds the closest note that matches the event time. </summary>
        /// <param name="eventTime">Time from the beginning of pattern</param>
        /// <param name="pattern">Searchable pattern</param>
        /// <returns>Item1 = note int the pattern, Item2 = delta, Item3 = duration from start of pattern</returns>
        public Tuple<Note, Rational, Rational> Evaluate(long eventTime, Pattern pattern) {
            // The UI may repeat the pattern to make it fit quarternote subpart, 
            // so the specified eventtime may be larger than the duration of a single pattern
            System.Diagnostics.Debug.WriteLine("Eval: EventTime " + eventTime);
            var evt = new Rational((int)eventTime, (int)QuarternotePeriod);

            if (evt.CompareTo(pattern.Duration) >= 0) {
                int repeats = (evt / pattern.Duration).Floor();
                evt -= new Rational(pattern.Duration.Numerator * repeats, pattern.Duration.Denominator);
            }

            // Iterate the pattern looking for the note with the smallest delta time
            // from the specified event time. 
            Rational minDelta = pattern.Duration, sum = Rational.Zero, absDelta = Rational.Abs(pattern.Duration);
            Note match = null;
            foreach (var note in pattern) {
                // The delta initially will be large and get smaller as we
                // scan the pattern until it reaches the minimum. 
                // At that point the delta will begin to grow again.

                match = note;
                var delta = sum - evt;

                //System.Diagnostics.Debug.WriteLine("Eval: delta: {0}/{1}, absdelta: {2}/{3}, sum = {4}/{5}, evt = {6}/{7}", 
                //    delta.Numerator, delta.Denominator, absDelta.Numerator, absDelta.Denominator, 
                //    sum.Numerator, sum.Denominator, evt.Numerator, evt.Denominator);

                if (absDelta.CompareTo(Rational.Abs(delta)) < 0) {
                    break;
                }

                minDelta = delta;
                absDelta = Rational.Abs(delta);
                sum += note.Duration;
            }

            return new Tuple<Note, Rational, Rational>(match, minDelta, sum);
        }
    }
}
